Skip to content

Add NGTS configuration + NGTS client#788

Open
SgtCoDFish wants to merge 1 commit intomasterfrom
ngts-flags
Open

Add NGTS configuration + NGTS client#788
SgtCoDFish wants to merge 1 commit intomasterfrom
ngts-flags

Conversation

@SgtCoDFish
Copy link
Copy Markdown
Contributor

@SgtCoDFish SgtCoDFish commented Mar 24, 2026

This will add initial support for NGTS. Auth is based on the existing Venafi Cloud client using a keypair.

I'm not really able to test this effectively because of various issues with the test env, but I think this is safe enough to merge as-is because it's not customer-facing yet (needs helm chart support before this is realistically usable)

Note there are several TODOs in this PR. They need to be clarified before we can expose this functionality to customers, but I think they're fine for now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB: this is pretty much copy+pasted from client_venafi_cloud.go because the logic is nearly identical. I refactored some of the shared logic out (util.go) but mostly this is the same thing with different names

This will add initial support for NGTS. Auth is based on the existing
Venafi Cloud client using a keypair.

Signed-off-by: Ashley Davis <ashley.davis@cyberark.com>
if res.OutputMode == NGTS && res.NGTSServerURL != "" {
log.Info("Using custom NGTS server URL (for testing)", "url", res.NGTSServerURL)
server = res.NGTSServerURL
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

propose (non-blocking): The final result will be the same and in the age of AI I'm not sure if anybody will look at this but having it written that way looks more readable to me.

Feel free to disregard it

 if res.OutputMode == NGTS {
     if res.NGTSServerURL != "" {
         log.Info("Using custom NGTS server URL (for testing)", "url", res.NGTSServerURL)
     } else if cfg.Server != "" {
         log.Info(fmt.Sprintf("ignoring the server field in the config file. In %s mode, use --ngts-server-url for testing.", NGTS))
     }
     server = res.NGTSServerURL
 }

return fmt.Errorf("failed to obtain NGTS access token: %w", err)
}

c.lock.Lock()
Copy link
Copy Markdown

@George-Yanev George-Yanev Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (non-blocking): We have a write lock but the code is actually sequential and we never do the read lock in the getValidAccessToken. I guess this was a legacy. Just pointing it out.

Comment on lines +592 to 608
if res.OutputMode == NGTS {
// In NGTS mode, use NGTSServerURL if provided, otherwise we'll use a default
// (which will be determined when creating the client)
server = res.NGTSServerURL
}
}

// For NGTS mode with custom server URL
if res.OutputMode == NGTS && res.NGTSServerURL != "" {
log.Info("Using custom NGTS server URL (for testing)", "url", res.NGTSServerURL)
server = res.NGTSServerURL
}

url, urlErr := url.Parse(server)
if urlErr != nil || url.Hostname() == "" {
if urlErr != nil || (url.Hostname() == "" && server != "") {
errs = multierror.Append(errs, fmt.Errorf("server %q is not a valid URL", server))
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

propose (non-blocking): Doing the same but IMHO more readable

 // In NGTS mode: ignore the config-file server field entirely; use only
 // --ngts-server-url when provided (default URL is derived from TSG ID
 // at client construction time).
 if res.OutputMode == NGTS {
     if res.NGTSServerURL != "" {
         log.Info("Using custom NGTS server URL (for testing)", "url", res.NGTSServerURL)
     } else if cfg.Server != "" {
         log.Info(fmt.Sprintf("ignoring the server field in the config file. In %s mode, use --ngts-server-url for testing.", NGTS))
     }
     server = res.NGTSServerURL
 }

 url, urlErr := url.Parse(server)
 // An empty server is valid: NGTS derives its URL from the TSG ID at client
 // creation; VenafiCloudVenafiConnection doesn't use one at all.
 if server != "" && (urlErr != nil || url.Hostname() == "") {
     errs = multierror.Append(errs, fmt.Errorf("server %q is not a valid URL", server))
 }

Comment on lines +125 to +128
ok, why := credentials.IsClientSet()
if !ok {
return nil, fmt.Errorf("%s", why)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

propose (non-blocking): Doesn't the IsClientSet and Validate on line 125 do the same. From what I'm seeing they do. If that's true does it make sense to remove the IsClientSet?

extra = fmt.Sprintf(" (possibly malformed TSG ID %q?)", tsgID)
}

return nil, fmt.Errorf("invalid SCM base URL %q: %s%s", baseURL, err, extra)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proposal: Shouldn't that be NGTS instead of SCM?

claims["iss"] = c.credentials.ClientID
claims["iat"] = time.Now().Unix()
claims["exp"] = time.Now().Add(time.Minute).Unix()
claims["aud"] = path.Join(c.baseURL.Host, ngtsAccessTokenEndpoint)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the NGTS endpoint has not been updated yet to accept anything other than "api.venafi.cloud/v1/oauth/token/serviceaccount" here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this working for the NGTS endpoint?

func parsePrivateKeyFromPEMFile(privateKeyFilePath string) (crypto.PrivateKey, error) {
pkBytes, err := os.ReadFile(privateKeyFilePath)
if err != nil {
return nil, fmt.Errorf("failed to fetch Venafi Cloud authentication private key %q: %s",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nil, fmt.Errorf("failed to fetch Venafi Cloud authentication private key %q: %s",
return nil, fmt.Errorf("unable to read private key for authentication %q: %s",

Because this logic applies to both Venafi Cloud and NGTS.

uploadURL.RawQuery = query.Encode()

klog.FromContext(ctx).V(2).Info(
"uploading data readings to SCM",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"uploading data readings to SCM",
"uploading data readings to NGTS",

Let's make it consistent

&cfg.TSGID,
"tsg-id",
"",
"The TSG (Tenant Security Group) ID for NGTS mode. Required when using --ngts.",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"The TSG (Tenant Security Group) ID for NGTS mode. Required when using --ngts.",
"The TSG (Tenant Service Group) ID for NGTS mode. Required when using --ngts.",

// using key pair authentication and send data to NGTS endpoints.
NGTSMode bool

// TSGID (--tsg-id) is the TSG (Tenant Security Group) ID for NGTS mode.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// TSGID (--tsg-id) is the TSG (Tenant Security Group) ID for NGTS mode.
// TSGID (--tsg-id) is the TSG (Tenant Service Group) ID for NGTS mode.

}

// PostDataReadingsWithOptions uploads data readings to the NGTS backend.
// The TSG ID is included in the upload path to identify the tenant security group.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// The TSG ID is included in the upload path to identify the tenant security group.
// The TSG ID is included in the upload path to identify the tenant service group.

return nil, fmt.Errorf("cannot create NGTSClient: %w", err)
}

if tsgID == "" {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SgtCoDFish Any additional validation that makes sense to be done at this point? I am pretty sure that we will have something in the Helm chart.
FYI, according to the link below, TSG ID is

Possible values: >= 10 characters and <= 10 characters, Value must match regular expression ^1[0-9]+$

https://pan.dev/scm/api/tenancy/delete-tenancy-v-1-tenant-service-groups-tsg-id/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants